﻿using System;
using System.Reflection.Emit;

namespace Aeon.Emulator.Decoding.Emitters
{
    /// <summary>
    /// Emitter for decoding an immediate integer.
    /// </summary>
    internal class LoadImmediate : Emitter
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the LoadImmediate class.
        /// </summary>
        /// <param name="state">Common emitter state information.</param>
        /// <param name="immediateSize">Size of the immediate in bytes.</param>
        /// <param name="valueExtend">Indicates whether the immediate should be sign-extended.</param>
        public LoadImmediate(EmitStateInfo state, int immediateSize, ValueExtend valueExtend)
            : base(state)
        {
            if(immediateSize != 1 && immediateSize != 2 && immediateSize != 4 && immediateSize != 6 && immediateSize != 8)
                throw new ArgumentException("Unsupported immediate size.", "immediateSize");

            this.ImmediateSize = immediateSize;
            this.ValueExtend = valueExtend;
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets the size of the immediate in bytes.
        /// </summary>
        public int ImmediateSize { get; private set; }
        /// <summary>
        /// Gets a value indicating whether the immediate should be sign-extended.
        /// </summary>
        public ValueExtend ValueExtend { get; private set; }
        /// <summary>
        /// Gets the type of local variable needed to store the operand value as a method argument.
        /// </summary>
        public override Type MethodArgType
        {
            get
            {
                if(this.ValueExtend == ValueExtend.Sign)
                    return GetSignedIntType(this.ImmediateSize);
                else
                    return GetUnsignedIntType(this.ImmediateSize);
            }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Emits IL for the operation.
        /// </summary>
        public sealed override void EmitLoad()
        {
            //il.LoadLocal(ipPointer);
            LoadIPPointer();
            bool signExtend = this.ValueExtend == Emitters.ValueExtend.Sign;

            switch(this.ImmediateSize)
            {
            case 1:
                il.Emit(signExtend ? OpCodes.Ldind_I1 : OpCodes.Ldind_U1);
                break;

            case 2:
                il.Emit(signExtend ? OpCodes.Ldind_I2 : OpCodes.Ldind_U2);
                break;

            case 4:
                il.Emit(signExtend ? OpCodes.Ldind_I4 : OpCodes.Ldind_U4);
                break;

            case 6:
            case 8:
                il.Emit(OpCodes.Ldind_I8);
                break;

            default:
                throw new InvalidOperationException();
            }

            IncrementIPPointer(this.ImmediateSize);
        }
        #endregion
    }
}
